﻿using System;
using System.Globalization;

namespace Gum.FizzBuzz
{
    /// <summary>
    /// Provides an implementation of IFizzBuzzJob that implements the classic FizzBuzz algorithm, but allows
    /// a different transform alogrithm to be provided if need be.
    /// </summary>
    public class FizzBuzzJob : IFizzBuzzJob
    {
        private Func<int, string> transformFunction;

        /// <summary>
        /// Creates a default FizzBuzzJob using the DefaultTransformFunction and specifying a range of 1 to 100
        /// </summary>
        public FizzBuzzJob() : this(1, 100)
        { }

        /// <summary>
        /// Creates a FizzBuzzJob using the DefaultTransformFunction using the range specified
        /// </summary>
        public FizzBuzzJob(int start, int end) : this(start, end, DefaultTransformFunction)
        { }

        /// <summary>
        /// Creates a FizzBuzzJob using the specified transform function and range 
        /// </summary>
        /// <exception cref="ArgumentNullException">Throws ArgumentNullException if transformFunction is null</exception>
        public FizzBuzzJob(int start, int end, Func<int, string> transformFunction)
        {
            Start = start;
            End = end;
            TransformFunction = transformFunction;
        }

        /// <summary>
        /// The starting value of the FizzBuzz range to process
        /// </summary>
        /// <remarks>
        /// If Start value is greater than end value, job counts down
        /// </remarks>
        public int Start { get; set; }

        /// <summary>
        /// The ending value of the FizzBuzz range to process
        /// </summary>
        /// <remarks>
        /// If End value is less than start value, job counts down
        /// </remarks>
        public int End { get; set; }

        /// <summary>
        /// Transforms an integer input into a string output
        /// </summary>
        /// <param name="value">The integer value to transform</param>
        /// <returns>The value transformed using the TransformFunction</returns>
        public string Transform(int value)
        { 
            return TransformFunction(value);
        }

        /// <summary>
        /// Specifies the transform function used by the Transform() method
        /// </summary>
        /// <remarks>The default value will be the DefaultTransformFunction</remarks>
        /// <exception cref="ArgumentNullException">Throws ArgumentNullException if value is null</exception>
        public Func<int, string> TransformFunction
        {
            get { return transformFunction; }
            set
            {
                if (null == value) throw new ArgumentNullException("value");
                transformFunction = value;
            }
        }

        /// <summary>
        /// The default FizzBuzz transform function
        /// </summary>
        /// <param name="value">The value to transform</param>
        /// <returns>The value of the result of the FizzBuzz algorithm</returns>
        /// <remarks>
        /// The traditional FizzBuzz algorithm transforms as follows:
        /// value % 3 and value % 5 -> "FizzBuzz"
        ///   else
        /// value % 3 -> "Fizz"
        ///   else
        /// value % 5 -> "Buzz"
        ///   else
        /// value as string
        /// </remarks>
        public static string DefaultTransformFunction(int value)
        {
            if (value % 3 == 0 && value % 5 == 0) return "FizzBuzz";
            if (value % 3 == 0) return "Fizz";
            if (value % 5 == 0) return "Buzz";
            return value.ToString(CultureInfo.InvariantCulture);
        }

    }
}